package com.nuo.common.excel.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.nuo.common.excel.service.BaseIService;
import com.nuo.common.exception.GlobalException;
import com.nuo.common.utils.ObjectUtil;
import com.nuo.modules.student.entity.StudentEntity;
import com.nuo.modules.teacher.entity.TeacherEntity;
import com.nuo.modules.teacher.service.TeacherService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;
import java.util.stream.Collectors;

/**
 * @description: TODO EasyExcel采用一行一行的解析模式，并将一行的解析结果以观察者的模式通知处理
 * @author nuo
 * @date 2022/11/8 0:36
 * @version 1.0
 */
@Slf4j
public class ExcelListener<T, S extends BaseIService<T>> extends AnalysisEventListener<T> {


    /**
     * 每隔5条存储数据库，实际使用中可以100条，然后清理list ，方便内存回收
     */
    private static final int BATCH_COUNT = 100;

    /**
     * 缓存的数据
     */
    private List<T> cachedList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
    /**
     * 假设这个是一个DAO，当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
     */
    private S s;
    private Class<T> t;
    // 教师导入传入全部list，加快判重速度，减少IO
    private List<TeacherEntity> teacherList;


    public ExcelListener() {
    }


    /**
     * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
     *
     * @param s
     */
    public ExcelListener(S s) {
        this.s = s;
    }

    public ExcelListener(Class<T> t, S s) {
        this.t = t;
        this.s = s;
    }

    // 教师导入传入全部list，加快判重速度，减少IO
    public ExcelListener(Class<T> t, S s,List<TeacherEntity> list) {
        this.t = t;
        this.s = s;
        this.teacherList=list;
    }

    /**
     * 这个每一条数据解析都会来调用
     *
     * @param t    one row value. Is is same as {@link AnalysisContext#readRowHolder()}
     * @param context
     */
    @Override
    public void invoke(T t, AnalysisContext context) {

        log.info("解析到一条数据:{}", JSON.toJSONString(t));

        // 空行不插入
        if (ObjectUtil.isNotNullEntity(t)) {

            // 校验数据
            validData(t,context);

            cachedList.add(t);

            log.info("cachedList.add(" + JSON.toJSONString(t) + ")");
        }else {
            //空行直接跳过了
            return;
        }


        // 达到BATCH_COUNT了，需要去存储一次数据库，防止数据几万条数据在内存，容易OOM
        if (cachedList.size() >= BATCH_COUNT) {
            saveData();
            // 存储完成清理 list
            cachedList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
        }
    }

    private void validData(T t, AnalysisContext context) {
        // 当前行数 加上首行
        Integer rowIndex = context.readRowHolder().getRowIndex()+1;
        //当属于是学生类，开始校验学生
        if (t.getClass().equals(StudentEntity.class)){
            //强转
            StudentEntity studentEntity=(StudentEntity)t;
            if (!StringUtils.isNotBlank(studentEntity.getStudentId())) throw new GlobalException("导入失败，第"+rowIndex+"行学生ID为空");
//            if (!StringUtils.isNotBlank(studentEntity.getUsername())) throw new GlobalException("导入失败，第"+rowIndex+"行姓名为空");
//            if (!StringUtils.isNotBlank(studentEntity.getSex())) throw new GlobalException("导入失败，第"+rowIndex+"行性别为空");
//            if (!StringUtils.isNotBlank(studentEntity.getTelephone())) throw new GlobalException("导入失败，第"+rowIndex+"行手机号为空");

        }

        if (t.getClass().equals(TeacherEntity.class)){
            //强转
            TeacherEntity teacherEntity = (TeacherEntity) t;
            if (!StringUtils.isNotBlank(teacherEntity.getTeacherId())) throw new GlobalException("导入失败，第"+rowIndex+"行教师ID为空");//教师ID
            if (!StringUtils.isNotBlank(teacherEntity.getUsername())) throw new GlobalException("导入失败，第"+rowIndex+"行姓名为空");//姓名
            if (!StringUtils.isNotBlank(teacherEntity.getSex())) throw new GlobalException("导入失败，第"+rowIndex+"行性别为空");//性别
            if (!StringUtils.isNotBlank(teacherEntity.getClassName())) throw new GlobalException("导入失败，第"+rowIndex+"行班级为空");//班级
            if (!StringUtils.isNotBlank(teacherEntity.getSchool())) throw new GlobalException("导入失败，第"+rowIndex+"行学校为空");//学校
            if (!StringUtils.isNotBlank(teacherEntity.getTelephone())) throw new GlobalException("导入失败，第"+rowIndex+"行学校为空");//学校

            List<TeacherEntity> list = teacherList.stream().filter(t1 -> t1.getTelephone().equals(teacherEntity.getTelephone())).collect(Collectors.toList());
            if (list.size()>0) throw new GlobalException("导入失败，第"+rowIndex+"行手机号已存在");//手机号


        }




    }

    /**
     * 所有数据解析完成了 都会来调用
     *
     * @param context
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据，确保最后遗留的数据也存储到数据库
        if (cachedList.size() > 0) {
            saveData();
        }
        log.info("所有数据解析完成！");
    }

    /**
     * 加上存储数据库
     */
    private void saveData() {
        log.info("{}条数据，开始存储数据库！", cachedList.size());
        s.saveOrUpdateBatch(cachedList, t);
        log.info("存储数据库成功！");
    }

}
